package com.cskaoyan.duolai.clean.housekeeping.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cskaoyan.duolai.clean.common.expcetions.ForbiddenOperationException;
import com.cskaoyan.duolai.clean.common.model.dto.PageDTO;
import com.cskaoyan.duolai.clean.common.utils.ObjectUtils;
import com.cskaoyan.duolai.clean.housekeeping.converter.RegionConverter;
import com.cskaoyan.duolai.clean.housekeeping.converter.RegionServeConverter;
import com.cskaoyan.duolai.clean.housekeeping.dto.RegionSimpleDTO;
import com.cskaoyan.duolai.clean.housekeeping.enums.HousekeepingStatusEnum;
import com.cskaoyan.duolai.clean.housekeeping.dao.mapper.CityDirectoryMapper;
import com.cskaoyan.duolai.clean.housekeeping.dao.mapper.RegionMapper;
import com.cskaoyan.duolai.clean.housekeeping.dao.mapper.RegionServeMapper;
import com.cskaoyan.duolai.clean.housekeeping.dao.entity.CityDirectoryDO;
import com.cskaoyan.duolai.clean.housekeeping.dao.entity.RegionDO;
import com.cskaoyan.duolai.clean.housekeeping.dao.entity.RegionServeDetailDO;
import com.cskaoyan.duolai.clean.housekeeping.request.RegionCommand;
import com.cskaoyan.duolai.clean.housekeeping.request.RegionPageRequest;
import com.cskaoyan.duolai.clean.housekeeping.dto.DisplayServeTypeDTO;
import com.cskaoyan.duolai.clean.housekeeping.dto.RegionDTO;
import com.cskaoyan.duolai.clean.housekeeping.dto.RegionServeDetailDTO;
import com.cskaoyan.duolai.clean.housekeeping.dto.ServeTypeHomeDTO;
import com.cskaoyan.duolai.clean.housekeeping.service.IConfigRegionService;
import com.cskaoyan.duolai.clean.housekeeping.service.IHomeService;
import com.cskaoyan.duolai.clean.housekeeping.service.IRegionServeService;
import com.cskaoyan.duolai.clean.housekeeping.service.IRegionService;
import com.cskaoyan.duolai.clean.mysql.utils.PageUtils;
import com.cskaoyan.duolai.clean.redis.constants.RedisConstants;
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 区域管理
 **/
@Service
public class RegionServiceImpl extends ServiceImpl<RegionMapper, RegionDO> implements IRegionService {
    @Resource
    private IConfigRegionService configRegionService;
    @Resource
    private CityDirectoryMapper cityDirectoryMapper;

    @Resource
    RegionConverter regionConverter;

    @Resource
    IRegionServeService regionServeService;

    @Resource
    IRegionService regionService;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    RegionServeMapper regionServeMapper;

    @Autowired
    RegionServeConverter regionServeConverter;



    /**
     * 区域新增
     *
     * @param regionCommand 插入更新区域
     */
    @Override
    @Transactional
    public void addRegion(RegionCommand regionCommand) {
        //1.校验城市编码是否重复
        LambdaQueryWrapper<RegionDO> queryWrapper = Wrappers.<RegionDO>lambdaQuery()
                .eq(RegionDO::getCityCode, regionCommand.getCityCode());
        Long count = baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new ForbiddenOperationException("区域对应的城市码不能重复");
        }

        //查询城市
        LambdaQueryWrapper<CityDirectoryDO> cityQueryWrapper = Wrappers.<CityDirectoryDO>lambdaQuery()
                .eq(CityDirectoryDO::getCityCode, regionCommand.getCityCode());
        CityDirectoryDO cityDirectoryDO = cityDirectoryMapper.selectOne(cityQueryWrapper);
        if (ObjectUtils.isNull(cityDirectoryDO)) {
            throw new ForbiddenOperationException("城市编码不存在");
        }

        //查询城市的排序位
        LambdaQueryWrapper<RegionDO> sortQueryWrapper = Wrappers.<RegionDO>lambdaQuery()
                .eq(RegionDO::getCityCode, regionCommand.getCityCode())
                .orderByDesc(RegionDO::getSortNum)
                .last("LIMIT 1");
        RegionDO maxSortRegion = baseMapper.selectOne(sortQueryWrapper);
        int sortNum = 1;
        if (ObjectUtils.isNotNull(maxSortRegion)) {
            sortNum = maxSortRegion.getSortNum() + 1;
        }

        //2.新增区域
        RegionDO regionDO = regionConverter.regionCommandToRegionDO(regionCommand);
        regionDO.setActiveStatus(HousekeepingStatusEnum.INIT.getStatus()); // 默认草稿状态
        regionDO.setSortNum(sortNum);
        baseMapper.insert(regionDO);

        //3.初始化区域配置
        configRegionService.init(regionDO.getId(), regionDO.getCityCode());
    }

    /**
     * 区域修改
     *
     * @param id           区域id
     * @param managerName  负责人姓名
     * @param managerPhone 负责人电话
     */
    @Override
    public void update(Long id, String managerName, String managerPhone) {
        // 查询区域是否存在
        RegionDO regionDO = baseMapper.selectById(id);
        if (ObjectUtils.isNull(regionDO)) {
            throw new ForbiddenOperationException("区域不存在");
        }

        // 更新区域信息
        LambdaUpdateWrapper<RegionDO> updateWrapper = Wrappers.<RegionDO>lambdaUpdate()
                .eq(RegionDO::getId, id)
                .set(RegionDO::getManagerName, managerName)
                .set(RegionDO::getManagerPhone, managerPhone);
        baseMapper.update(null, updateWrapper);
    }

    /**
     * 区域删除
     *
     * @param id 区域id
     */
    @Override
    @Transactional
    public void deleteById(Long id) {
        // 区域信息
        RegionDO regionDO = baseMapper.selectById(id);
        if (ObjectUtils.isNull(regionDO)) {
            throw new ForbiddenOperationException("区域不存在");
        }

        // 草稿状态方可删除
        if (!HousekeepingStatusEnum.INIT.equals(regionDO.getActiveStatus())) {
            throw new ForbiddenOperationException("只有处于草稿状态的区域可以被删除");
        }

        // 删除
        baseMapper.deleteById(id);
    }

    /**
     * 分页查询
     *
     * @param regionPageQueryReqDTO 查询条件
     * @return 分页结果
     */
    @Override
    public PageDTO<RegionDTO> getPage(RegionPageRequest regionPageQueryReqDTO) {
        // 创建分页对象
        Page<RegionDO> page = PageUtils.parsePageQuery(regionPageQueryReqDTO, RegionDO.class);

        // 查询数据
        Page<RegionDO> regionPage = baseMapper.selectPage(page, null);

        // 转换为DTO
        List<RegionDTO> regionDTOList = regionConverter.regionDOsToRegionDTOs(regionPage.getRecords());

        // 返回分页结果
        return PageUtils.toPage(regionPage, regionDTOList);
    }

    /**
     * 已开通服务区域列表
     *
     * @return 区域列表
     */
    @Override
    public List<RegionSimpleDTO> queryActiveRegionListDb() {

        LambdaQueryWrapper<RegionDO> queryWrapper = Wrappers.<RegionDO>lambdaQuery()
                .eq(RegionDO::getActiveStatus, HousekeepingStatusEnum.ENABLE.getStatus())
                .orderByAsc(RegionDO::getSortNum);
        List<RegionDO> regionDOList = baseMapper.selectList(queryWrapper);
        return regionConverter.regionDOsToRegionSimpleDTOs(regionDOList);

    }

    /**
     * 区域启用
     *
     * @param id 区域id
     */
    @CachePut(cacheNames = RedisConstants.CacheName.REGION_CACHE, key = "'ACTIVE_REGIONS'")
    @Override
    public List<RegionSimpleDTO> active(Long id) {
        // 区域信息
        RegionDO regionDO = baseMapper.selectById(id);
        if (ObjectUtils.isNull(regionDO)) {
            throw new ForbiddenOperationException("区域不存在");
        }

        // 草稿或禁用状态方可启用
        if (regionDO.getActiveStatus() == null || !(HousekeepingStatusEnum.INIT.equals(regionDO.getActiveStatus()) ||
                HousekeepingStatusEnum.DISABLE.equals(regionDO.getActiveStatus()))) {
            throw new ForbiddenOperationException("只有处于草稿或者禁用状态的区域可以被启用");
        }

        // 更新启用状态
        LambdaUpdateWrapper<RegionDO> updateWrapper = Wrappers.<RegionDO>lambdaUpdate()
                .eq(RegionDO::getId, id)
                .set(RegionDO::getActiveStatus, HousekeepingStatusEnum.ENABLE.getStatus());
        baseMapper.update(null, updateWrapper);

        return queryActiveRegionListDb();
    }

    /**
     * 区域禁用
     *
     * @param id 区域id
     */
    @CachePut(cacheNames = RedisConstants.CacheName.REGION_CACHE, key = "'ACTIVE_REGIONS'")
    @Override
    public List<RegionSimpleDTO> deactivate(Long id) {
        // 区域信息
        RegionDO regionDO = baseMapper.selectById(id);
        if (ObjectUtils.isNull(regionDO)) {
            throw new ForbiddenOperationException("区域不存在");
        }

        // 启用状态方可禁用
        if (regionDO.getActiveStatus() == null || !HousekeepingStatusEnum.ENABLE.equals(regionDO.getActiveStatus())) {
            throw new ForbiddenOperationException("只有启用状态的区域可以被禁用");
        }

        // 如果禁用区域下有上架的服务则无法禁用
        int serveCount = regionServeService.queryServeCountByRegionIdAndSaleStatus(id, 2); // 2表示上架状态
        if (serveCount > 0) {
            throw new ForbiddenOperationException("该区域下有上架的服务，无法禁用");
        }

        // 更新禁用状态
        LambdaUpdateWrapper<RegionDO> updateWrapper = Wrappers.<RegionDO>lambdaUpdate()
                .eq(RegionDO::getId, id)
                .set(RegionDO::getActiveStatus, HousekeepingStatusEnum.DISABLE.getStatus());
        baseMapper.update(null, updateWrapper);

        return queryActiveRegionListDb();
    }

    /**
     * 已开通服务区域列表
     *
     * @return 区域简略列表
     */
    @Cacheable(cacheNames = RedisConstants.CacheName.REGION_CACHE, key = "'ACTIVE_REGIONS'")
    @Override
    public List<RegionSimpleDTO> queryActiveRegionListCache() {
        return regionService.queryActiveRegionListDb();
    }


    @Autowired
    RegionServeConverter converter;
    /**
     * 刷新区域id相关缓存：首页图标、热门服务、服务分类
     *
     * @param regionId 区域id
     */
    @Override
    public void refreshRegionRelateCaches(Long regionId) {

        // 1. 首页服务列表缓存
        regionServeService.refreshFirstPageRegionServeList(regionId);

        // 2. 精选推荐列表
        regionServeService.refreshFistPageHotServeList(regionId);

        // 3. 服务类型列表
        regionServeService.refreshFirstPageServeTypeList(regionId);

        // 4. 区域服务详情

        // 4.1 从数据库中查询最新的区域服务详情列表
        List<RegionServeDetailDO> regionServeDetailByRegionId
                = regionServeMapper.findRegionServeDetailByRegionId(regionId);

        List<Long> dbDetailIds = regionServeDetailByRegionId.stream().map(regionServeDetailDO -> regionServeDetailDO.getId()).collect(Collectors.toList());


        // 4.2 获取缓存中的区域服务数据
        RMap<Long, RegionServeDetailDTO> detailMap
                = redissonClient.getMap(RedisConstants.CacheName.REGION_SERVE_DETAIL);

        // 根据区域过滤出只属于目标区域的区域服务项
        Map<Long, RegionServeDetailDTO> regionDetailMap = detailMap.entrySet().stream().filter(entry -> {
            RegionServeDetailDTO value = entry.getValue();
            return value.getRegionId() != null && value.getRegionId().equals(regionId);

        }).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));


        // 4.3 针对数据库没有，缓存中有的数据(将数据库中已经下架的区域服务项从缓存中删除)
        List<Long> toDeleteIds = regionDetailMap.keySet().stream()
                .filter(cacheDetailId -> !dbDetailIds.contains(cacheDetailId)).collect(Collectors.toList());

        // 4.4 删除缓存中待删除的区域服务详情
        toDeleteIds.forEach(id -> {
            detailMap.remove(id);
        });


        // 4.5 用数据库中最新的区域服务详情数据，更新缓存
        regionServeDetailByRegionId.forEach(regionServeDetailDO -> {
            RegionServeDetailDTO regionServeDetailDTO
                    = regionServeConverter.regionServeDetailDO2DTO(regionServeDetailDO);
            detailMap.put(regionServeDetailDTO.getId(), regionServeDetailDTO);
        });

    }

}
